跳到主要内容

包装异步代码为同步代码

在本节我们将学到如何将一个异步回调函数逐步包装成同步代码。

异步代码

这是一段十分简单的代码,在函数内使用了一个 get 获取网页内容,如果我们想在获取内容之后进行一些处理需要写在 onload 函数内,这种代码就叫做异步代码,当执行完请求后再回调处理的函数,这里是 onload 函数。

function GetMess() {
GM_xmlhttpRequest({
url: "https://bbs.tampermonkey.net.cn/",
method: "GET",
headers: {},
onload: function (xhr) {
console.log(xhr.responseText);
},
});
}
GetMess();

有时候这样非常麻烦,那有没有什么办法改为同步代码呢?解决方案就是使用 Promise

let p = new Promise((resolve, reject) => {});

我们首先创建一个 Promise,当 Promise 不调用 resolve 和 reject 结束状态,那将一直是 pengding 状态,只有调用 resolve 或 reject 的时候,才可以结束状态并返回 resolve 或 reject 函数内的值,所以我们需要使用 resolve/reject 函数

现在我们在 onload 后使用 resolve 结束这个 Promise 状态,并返回 xhr.responseText 的值

function GetMess() {
let p = new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://bbs.tampermonkey.net.cn/",
method: "GET",
headers: {},
onload: function (xhr) {
resolve(xhr.responseText);
},
});
});
}
GetMess();

我们再结合 async 和 await 的语法糖就可以了

await 需要一个 Promise,所以我们需要将我们构建的这个 Promise 返回

添加 await GetMess()后,GetMess 函数会返回一个 Promise,Async 和 Await 语法糖会使 Promise 阻塞直至完成状态后,并将 resolve 内填入的值赋值给 text 变量上。

需要注意的是我们不可以在全局作用域内使用 await,目前并未实现该特性,只有在 async 的函数内才可以使用 await。

function GetMess() {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://bbs.tampermonkey.net.cn/",
method: "GET",
headers: {},
onload: function (xhr) {
resolve(xhr.responseText);
},
});
});
}
async function GetPromiseAndWait() {
let text = await GetMess();
}
GetPromiseAndWait();

由于 GetPromiseAndWait 使用了 Async 语法糖,所以调用他现在也会返回一个 Promise 变量,如果我们想要同步获取他返回的值,也需要使用 Async 与 Await

function GetMess() {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://bbs.tampermonkey.net.cn/",
method: "GET",
headers: {},
onload: function (xhr) {
resolve(xhr.responseText);
},
});
});
}
async function GetPromiseAndWait() {
let text = await GetMess();
return "我处理完了";
}
function WantGetFinallyText() {
let data = GetPromiseAndWait();
// 输出一个Promise对象
console.log("WantGetFinallyText", data);
}

WantGetFinallyText();

我们可以看到console.log输出了一个promise,这个值是pengding,因为我们调用了GetPromiseAndWait,他还没有执行结束,直接将GetPromiseAndWait所生成的Promise对象赋值给了data

如果我们想要获取到GetPromiseAndWait的值,需要让这个函数执行完毕得到返回值,再继续执行WantGetFinallyText函数。这个时候我们需要对调用这个函数返回的Promise使用Async/Await语法糖,来让这个Promise形成阻塞。

function GetMess(){
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url:"https://bbs.tampermonkey.net.cn/",
method :"GET",
headers: {
},
onload:function(xhr){
resolve(xhr.responseText)

}
});
})
}
async function GetPromiseAndWait(){
let text=await GetMess()
return '我处理完了'
}
async function WantGetFinallyText(){
let data=await GetPromiseAndWait()
// 输出一个【WantGetFinallyText 我处理完了】
console.log('WantGetFinallyText',data)
}

WantGetFinallyText()

这个时候await就会阻塞住GetPromiseAndWait一直到履行状态再将其值赋值给data,并且return会使函数变为履行状态,也就是说await会使函数一直阻塞到GetPromiseAndWait的return函数返回内容为止。

注意,还有一个知识点

就是GetPromiseAndWait如果return一个Promise,那这个函数返回的将是这个Promise,await阻塞的也是这个Promise的值。

这个概念我们之前也讲过,当async函数return一个值时,会将其包装成一个Promise,当返回一个Promise时,将直接返回这个Promise

那么到这里,你就学会了Promise以及多层Async函数的执行。